/**
 * 工具函數測試文件
 */

import { 
  debounce, 
  throttle, 
  deepClone, 
  formatDate, 
  formatFileSize,
  generateId,
  validatePhone,
  validateIdCard,
  validateEmail,
  uniqueArray,
  delay,
  retry,
  get,
  set
} from '../utils/helpers'

describe('工具函數測試', () => {
  describe('debounce', () => {
    it('應該正確防抖', (done) => {
      let callCount = 0
      const debouncedFn = debounce(() => {
        callCount++
      }, 100)
      
      debouncedFn()
      debouncedFn()
      debouncedFn()
      
      setTimeout(() => {
        expect(callCount).toBe(1)
        done()
      }, 150)
    })
  })
  
  describe('throttle', () => {
    it('應該正確節流', (done) => {
      let callCount = 0
      const throttledFn = throttle(() => {
        callCount++
      }, 100)
      
      throttledFn()
      throttledFn()
      throttledFn()
      
      setTimeout(() => {
        expect(callCount).toBe(1)
        done()
      }, 50)
    })
  })
  
  describe('deepClone', () => {
    it('應該深拷貝對象', () => {
      const original = {
        a: 1,
        b: { c: 2, d: [3, 4] },
        e: new Date('2023-01-01')
      }
      
      const cloned = deepClone(original)
      
      expect(cloned).not.toBe(original)
      expect(cloned.b).not.toBe(original.b)
      expect(cloned.b.d).not.toBe(original.b.d)
      expect(cloned.e).not.toBe(original.e)
      expect(cloned).toEqual(original)
    })
  })
  
  describe('formatDate', () => {
    it('應該正確格式化日期', () => {
      const date = new Date('2023-01-01T12:30:45')
      
      expect(formatDate(date, 'YYYY-MM-DD')).toBe('2023-01-01')
      expect(formatDate(date, 'HH:mm:ss')).toBe('12:30:45')
      expect(formatDate(date)).toBe('2023-01-01 12:30:45')
    })
  })
  
  describe('formatFileSize', () => {
    it('應該正確格式化文件大小', () => {
      expect(formatFileSize(1024)).toBe('1 KB')
      expect(formatFileSize(1024 * 1024)).toBe('1 MB')
      expect(formatFileSize(0)).toBe('0 Bytes')
    })
  })
  
  describe('generateId', () => {
    it('應該生成指定長度的ID', () => {
      const id1 = generateId(8)
      const id2 = generateId(16)
      
      expect(id1).toHaveLength(8)
      expect(id2).toHaveLength(16)
      expect(id1).not.toBe(id2)
    })
  })
  
  describe('validatePhone', () => {
    it('應該正確驗證手機號', () => {
      expect(validatePhone('13800138000')).toBe(true)
      expect(validatePhone('12345678901')).toBe(false)
      expect(validatePhone('1380013800')).toBe(false)
    })
  })
  
  describe('validateIdCard', () => {
    it('應該正確驗證身份證號', () => {
      expect(validateIdCard('110101199001011234')).toBe(true)
      expect(validateIdCard('11010119900101123')).toBe(false)
      expect(validateIdCard('11010119900101123X')).toBe(true)
    })
  })
  
  describe('validateEmail', () => {
    it('應該正確驗證郵箱', () => {
      expect(validateEmail('test@example.com')).toBe(true)
      expect(validateEmail('test@example')).toBe(false)
      expect(validateEmail('test.example.com')).toBe(false)
    })
  })
  
  describe('uniqueArray', () => {
    it('應該正確去重數組', () => {
      const array = [1, 2, 2, 3, 3, 4]
      const objectArray = [
        { id: 1, name: 'A' },
        { id: 2, name: 'B' },
        { id: 1, name: 'A' }
      ]
      
      expect(uniqueArray(array)).toEqual([1, 2, 3, 4])
      expect(uniqueArray(objectArray, 'id')).toHaveLength(2)
    })
  })
  
  describe('delay', () => {
    it('應該正確延遲執行', (done) => {
      const start = Date.now()
      
      delay(100).then(() => {
        const end = Date.now()
        expect(end - start).toBeGreaterThanOrEqual(100)
        done()
      })
    })
  })
  
  describe('retry', () => {
    it('應該正確重試函數', async () => {
      let attempts = 0
      const fn = async () => {
        attempts++
        if (attempts < 3) {
          throw new Error('失敗')
        }
        return '成功'
      }
      
      const result = await retry(fn, 3, 10)
      
      expect(result).toBe('成功')
      expect(attempts).toBe(3)
    })
    
    it('應該在最大重試次數後拋出錯誤', async () => {
      const fn = async () => {
        throw new Error('總是失敗')
      }
      
      await expect(retry(fn, 2, 10)).rejects.toThrow('總是失敗')
    })
  })
  
  describe('get', () => {
    	it('应该安全地获取对象属性值', () => {
      const obj = { a: { b: { c: 1 } } }
      
      expect(get(obj, 'a.b.c')).toBe(1)
      expect(get(obj, 'a.b.d', 'default')).toBe('default')
      expect(get(obj, 'x.y.z')).toBeUndefined()
    })
  })
  
  describe('set', () => {
    	it('应该安全地设置对象属性值', () => {
      const obj = {}
      
      set(obj, 'a.b.c', 1)
      
      expect(obj).toEqual({ a: { b: { c: 1 } } })
    })
  })
})
